Polymorfi
Teori
Samme grænseflade til forskellige datatyper. – systime
Overordnet set kan polymorfi udføres under kompilering og under programkørsel. Første udgave vil føre til større programstørrelse og anden udgave vil være mindre effektiv under kørsel. I Python vil der stort set altid være tale om polymorfi under programkørsel1.
1 I Python er udfordringen med datatyper ofte det omvendte end sprog med statisk bundne datatyper. Nemlig at sikre sig, at datatyper har de nødvendige metoder/attributter. Det er kendt som “duck typing”.
I Rust vil træk (traits) blive brugt i alle nedenstående eksempler. Et træk kan opfattes som forventning om noget en datatype kan udføre. Polymorfi kan også opnås vha. metaprogrammering.

For visualisering af polymorfi, se afsnittene om trait og nedarvning i klassediagrammer.
Polymorfi under kompilering
Polymorfi der udføres under kompilering er også kendt som “static dispatch”.
Overlæsse
En funktion som kan anvendes på argumenter af forskellige typer og antal via flere implementeringer – systime [1]
I Python kan operatorer overlæsses vha. de indbyggede metoder for operationer. Andre metoder kan overlæsses som i eksemplet fra Systime refereret ovenfor.
I Rust kan overlæsning af operatorer (men ikke andre funktioner) udføres ved at implementere funktionerne defineret i std::ops for flere datatyper [2].
use std::ops::Add;
#[derive(Debug, Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
// Overlæsning af + operator
impl Add for Point {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
fn main() {
let a = Point { x: 1, y: 0 };
let b = Point { x: 2, y: 3 };
println!("{:?}", a + b);
}Generisk funktion
En funktion som kan anvendes på argumenter af forskellig type via én implementering uden at tage hensyn til specifikke typer. – systime [3]
Kræver intet specielt i Python.
I Rust angives generiske datatyper vha. <T> og anvendes ofte sammen med træk (traits) der angiver hvilke kendetegn som typen skal have [4].
use std::ops::Add;
// Typen T kræver trait: at operator + er defineret og at output også er af typen T.
fn sum<T: Add<Output = T>>(a: T, b: T) -> T {
return a + b;
}
fn main() {
println!("{}", sum(3, 5)); // Sum med heltal
println!("{}", sum(3.2, 5.5)); // Sum med kommatal
}Polymorfi under kørsel
Polymorfi der udføres under kørsel er også kendt som dynamic dispatch (hvilket også muliggør dynamisk binding [5]). I Python er metoder og attributer som udgangspunkt dynamisk bundet.
I Rust kan fællestræk for flere strukturer defineres vha. egne træk (traits). Derefter kan polymorfi udføres under programkørsel vha. objekter med fællestræk (trait objects), som oprettes ved at lave en pointer (fx en reference & eller Box) med dyn nøgleordet og til sidst det træk som objektet skal overholde, fx &dyn firkant.
// Fællestræk defineres som trait
trait Greet {
fn greet(&self);
}
// Human struct
struct Human {
name: String,
}
// Implementer fællestrækket for Human
impl Greet for Human {
fn greet(&self) {
println!("Hello I'm {}", self.name)
}
}
// Duck struct
struct Duck {}
// Implementer fællestrækket for Duck
impl Greet for Duck {
fn greet(&self) {
println!("Quack!")
}
}
fn main() {
// Hvis objekterne oprettes som normalt kan greet bruges som normale metoder
let human = Human {
name: String::from("Superman"),
};
let duck = Duck {};
// Polymorfien her udføres stadig under kompilering
human.greet();
duck.greet();
// For at opnå polymorfi under kørsel bruges en pointer sammen med "dyn"
let mut greeter: &dyn Greet = &Human {
name: String::from("Daredevil"),
};
greeter.greet();
// Vi kan lave variablen om til andet der implementerer fællestrækket
greeter = &duck;
greeter.greet();
// Vi kan nu også lave en vector med forskellige datastrukturer hvor vi kræver at et fællestræk er opfyldt.
// Herunder er det gjort vha. Box<dyn Greet>
let greeters: Vec<Box<dyn Greet>> = vec![
Box::new(Human {
name: String::from("Batman"),
}),
Box::new(Duck {}),
];
for greeter in greeters {
greeter.greet();
}
}Nedarvning
Rust gør ikke brug af nedarvning [6], men i mange andre sprog er nedarvning et typisk værktøj til at opnå polymorfi. Konceptet er at en klasse kan nedarve fra en “forælder” hvis det er en mere specifik udgave af forælderen. Dermed kan fællestræk være defineret for forælderen, mens mere specifikke træk er defineret for børnene [7].
Et kvadrat (underklasse) og en trapez (underklasse) er begge firkanter (superklasse).